import * as THREE from 'three'
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
import { WuTi } from "./WuTi"
import { huanJing } from '../../huanJing/HuanJing.js'
import { gengXinJiHe } from '../../gengXin/gengXin.js'
import { wuTiCaoZuo, wuLiWorker } from '../../wuLi/WuLiWorker.js'

// 柔体
export class RouTi extends WuTi {
	zhiLiang = null
	wuTiLeiXing = '柔体'
	shiRouTi = true
	xingZhuang = null
	#pengZhuangZhuangTai = false
	tongBuWuLi = true // 是否从物理世界同步状态
	
	jiaZaiJuLi = 100 // 加载距离 小于此值加载
	jiaZaiZhuangTai = false // true：已加载， false：未加载
	fu = null // 父物体
	;['shouLiXingXing'] = null // 受力行星
	zhongLi = null
	
	constructor(moXing, zhiLiang = 0.9) {
		super(moXing)
		
		Object.defineProperty(this, 'zhiLiang', {
			value: zhiLiang
		})
	}
	
	tianJiaPengZhuangTi() {
		this.pengZhuangZhuangTai = true
		
		let chuLiPengZhuangTi = (pzt) => {
			let xingZhuang = pzt.userData.xingZhuang
			let jieGuo = { xingZhuang }
			
			// PlaneGeometry
			if (xingZhuang == '面') {
				let chiCun = jieGuo.chiCun = pzt.geometry.parameters

				let widthHalf = chiCun.width / 2
				let heightHalf = chiCun.height / 2
				
				const zuoShang = new THREE.Vector3(-widthHalf, heightHalf, 0);
				const youShang = new THREE.Vector3(widthHalf, heightHalf, 0);
				const zuoXia = new THREE.Vector3(-widthHalf, -heightHalf, 0);
				const youXia = new THREE.Vector3(widthHalf, -heightHalf, 0);
				
				this.moXing.localToWorld(zuoShang)
				this.moXing.localToWorld(youShang)
				this.moXing.localToWorld(zuoXia)
				this.moXing.localToWorld(youXia)
				
				jieGuo.mian = {
					zuoShang: zuoShang.toArray(),
					youShang: youShang.toArray(),
					zuoXia: zuoXia.toArray(),
					youXia: youXia.toArray()
				}
			}
			// Line
			if (xingZhuang == '线') {
				let { moWeiZhi, duanShu } = pzt.userData.xian
				this.moXing.parent.localToWorld(moWeiZhi)

				jieGuo.xian = {
					moWeiZhi: moWeiZhi.toArray(),
					duanShu
				}
			}
			if (xingZhuang == '体') {
				processGeometry( pzt.geometry )
				pzt.frustumCulled = false

				jieGuo.ti = {
					dingDian: pzt.geometry.ammoVertices,
					suoYin: pzt.geometry.ammoIndices
				}
			}
			
			this.xingZhuang = xingZhuang
	
			return jieGuo
		}
	
		let caoZuo = {
			mingZi: 'tianJiaWuTi',
			canShu: {
				wuTiLeiXing: this.wuTiLeiXing,
				bianHao: this.bianHao,
				mingZi: this.mingZi,
				zhiLiang: this.zhiLiang,
				weiZhi: this.moXing.getWorldPosition(new THREE.Vector3()).toArray(),
				xuanZhuan: this.moXing.parent.getWorldQuaternion(new THREE.Quaternion()).toArray()
			}
		}
		caoZuo.canShu.pengZhuangTi = chuLiPengZhuangTi(this.moXing)
		wuTiCaoZuo[this.bianHao] = [caoZuo]
	}
	// 移除碰撞体出物理引擎
	yiChuPengZhuangTi() {
		this.pengZhuangZhuangTai = false

		let caoZuo = { mingZi: 'yiChuWuTi', canShu: { bianHao: this.bianHao } }
		if (wuTiCaoZuo[this.bianHao]) wuTiCaoZuo[this.bianHao].push(caoZuo)
	}
	/**
	 * 设置碰撞体 本地坐标需转世界坐标
	 */
	sheZhiPengZhuangTi(sheZhiCanShu) {
		if (!this.pengZhuangZhuangTai) return

		let caoZuoShuZu = wuTiCaoZuo[this.bianHao]
		
		if (caoZuoShuZu) {
			let sheZhiCaoZuo = caoZuoShuZu.find(caoZuo => caoZuo.mingZi == 'sheZhiWuTi')
			if (sheZhiCaoZuo) {
				let canShu = sheZhiCaoZuo.canShu
				canShu.bianHao = this.bianHao
				canShu.mingZi = this.mingZi

				if (sheZhiCanShu.shouLi) canShu.shouLi = sheZhiCanShu.shouLi.toArray()
				if (sheZhiCanShu.mao) canShu.mao = sheZhiCanShu.mao
			} else {
				let caoZuo = {
					mingZi: 'sheZhiWuTi',
					canShu: {
						bianHao: this.bianHao,
						mingZi: this.mingZi,
						shouLi: sheZhiCanShu.shouLi?.toArray(),
						mao: sheZhiCanShu.mao // Array<{ suoYin: Number, wuTiBianHao: String }>
					}
				}
				
				wuTiCaoZuo[this.bianHao].push(caoZuo)
			}
		}
	}
	
	set pengZhuangZhuangTai(canShu) {
		this.#pengZhuangZhuangTai = canShu
	}
	get pengZhuangZhuangTai() {
		return this.#pengZhuangZhuangTai
	}
	// 受行星的重力
	// 因为柔体是世界坐标系且position不变，所以重力计算不准确
	shouZhongLi() {
		if (this.shiXingXing || this.shiHengXing) return
		let quanBuWuTi = WuTi.quanBuWuTi
		let zuiDuanjuLi = Infinity
		let xingXing = null
		let zhongLi = null
		let ziShenWeiZhi = new THREE.Vector3() // 自身位置
		let xingXingWeiZhi = new THREE.Vector3() // 行星位置
		let fangXiang = new THREE.Vector3()
		this.moXing.getWorldPosition(ziShenWeiZhi)

		for (let i = 0, il = quanBuWuTi.length; i < il; i++) {
			let wuTi = quanBuWuTi[i]
			if (!wuTi.shiXingXing) continue
			let weiZhi = new THREE.Vector3()
			wuTi.moXing.getWorldPosition(weiZhi)
			let juLi = ziShenWeiZhi.distanceTo(weiZhi)
			if (juLi < zuiDuanjuLi && juLi < wuTi.yinLiJuLi) {
				xingXingWeiZhi = weiZhi
				zuiDuanjuLi = juLi
				xingXing = wuTi
			}
		}
		this.shouLiXingXing = xingXing
		
		if (xingXing) {
			zhongLi = fangXiang.subVectors(xingXingWeiZhi, ziShenWeiZhi).normalize().multiplyScalar(0.2)
			this.sheZhiPengZhuangTi({ shouLi: zhongLi })
		} else {
			this.sheZhiPengZhuangTi({ shouLi: new THREE.Vector3(0, 0 ,0) })
		}
		this.zhongLi = zhongLi
	}
	// 从物理引擎同步数据
	tongBu(wuTiCanShu) {
		if (!this.tongBuWuLi || wuLiWorker.tingZhiMoNiZhuangTai) return

		let { dingDian, faXian } = wuTiCanShu
		let geometry = this.moXing.geometry

		if (this.moXing.userData.xingZhuang == '体') {
			const volumePositions = geometry.attributes.position.array;
			const volumeNormals = geometry.attributes.normal.array;
			const association = geometry.ammoIndexAssociation;
			const numVerts = association.length;
			
			for ( let i = 0; i < numVerts; i ++ ) {
				const x = dingDian[i * 3];
				const y = dingDian[i * 3 + 1];
				const z = dingDian[i * 3 + 2];
				const nx = faXian[i * 3];
				const ny = faXian[i * 3 + 1];
				const nz = faXian[i * 3 + 2];

				const assocVertex = association[ i ];
				for ( let j = 0, jl = assocVertex.length; j < jl; j ++ ) {
					let indexVertex = assocVertex[ j ];
					volumePositions[ indexVertex ] = x;
					volumeNormals[ indexVertex ] = nx;
					indexVertex ++;
					volumePositions[ indexVertex ] = y;
					volumeNormals[ indexVertex ] = ny;
					indexVertex ++;
					volumePositions[ indexVertex ] = z;
					volumeNormals[ indexVertex ] = nz;
				}
			}
		} else {
			geometry.attributes.position.array = new Float32Array(dingDian)
			this.moXing.updateMatrixWorld(true)
			geometry.applyMatrix4(this.moXing.matrixWorld.clone().invert())
			geometry.computeVertexNormals(); // 柔体线计算法线会卡顿，可能是性能问题 
		}
		geometry.attributes.position.needsUpdate = true;
		geometry.attributes.normal.needsUpdate = true;
	}
	// 帧更新
	gengXin(jianGeShi) {
		this.shouZhongLi()
	}
	// 加载物体，加载模型进父模型
	jiaZai() {
		this.jiaZaiZhuangTai = true
		let fuMoXing = this.fu?.moXing ?? huanJing.scene
		fuMoXing.add(this.moXing)
		this.tianJiaPengZhuangTi()
		WuTi.quanBuWuTi.push(this)
		gengXinJiHe.set(this, this.gengXin.bind(this))
	}
	// 卸载物体
	xieZai() {
		this.jiaZaiZhuangTai = false
		this.moXing.removeFromParent()
		this.yiChuPengZhuangTi()
		gengXinJiHe.delete(this)
		let suoYin = WuTi.quanBuWuTi.findIndex(w => w == this)
		if (suoYin > -1) WuTi.quanBuWuTi.splice(suoYin, 1)
	}
	// 销毁物体
	xiaoHui() {
		this.xieZai()
		
		function xh(a) {
			a.geometry?.dispose()
			a.material?.map?.dispose()
			a.material?.dispose()
		}
		this.moXing.traverse(xh)
	}
	// 复制物体
	fuZhi() {
		let moXing = SkeletonUtils.clone(this.moXing)
		return new this.constructor(moXing)
	}
}


function processGeometry( bufGeometry ) {

	// Ony consider the position values when merging the vertices
	const posOnlyBufGeometry = new THREE.BufferGeometry();
	posOnlyBufGeometry.setAttribute( 'position', bufGeometry.getAttribute( 'position' ) );
	posOnlyBufGeometry.setIndex( bufGeometry.getIndex() );

	// Merge the vertices so the triangle soup is converted to indexed triangles
	const indexedBufferGeom = BufferGeometryUtils.mergeVertices( posOnlyBufGeometry );

	// Create index arrays mapping the indexed vertices to bufGeometry vertices
	mapIndices( bufGeometry, indexedBufferGeom );

}

function isEqual( x1, y1, z1, x2, y2, z2 ) {

	const delta = 0.000001;
	return Math.abs( x2 - x1 ) < delta &&
			Math.abs( y2 - y1 ) < delta &&
			Math.abs( z2 - z1 ) < delta;

}

function mapIndices( bufGeometry, indexedBufferGeom ) {

	// Creates ammoVertices, ammoIndices and ammoIndexAssociation in bufGeometry

	const vertices = bufGeometry.attributes.position.array;
	const idxVertices = indexedBufferGeom.attributes.position.array;
	const indices = indexedBufferGeom.index.array;

	const numIdxVertices = idxVertices.length / 3;
	const numVertices = vertices.length / 3;

	bufGeometry.ammoVertices = idxVertices;
	bufGeometry.ammoIndices = indices;
	bufGeometry.ammoIndexAssociation = [];

	for ( let i = 0; i < numIdxVertices; i ++ ) {

		const association = [];
		bufGeometry.ammoIndexAssociation.push( association );

		const i3 = i * 3;

		for ( let j = 0; j < numVertices; j ++ ) {

			const j3 = j * 3;
			if ( isEqual( idxVertices[ i3 ], idxVertices[ i3 + 1 ], idxVertices[ i3 + 2 ],
				vertices[ j3 ], vertices[ j3 + 1 ], vertices[ j3 + 2 ] ) ) {

				association.push( j3 );

			}

		}

	}

}